#pragma once

using namespace System;
using namespace NUnit::Framework;
using namespace LatoolNet;

namespace LatoolNetTest {

	[TestFixture]
	public ref class ComplexGeneralTridiagonalMatrixTest {
	public:
		[Test]
		void TestSetGetValue() {

			int rownum = 6;
			int colnum = 6;

			ComplexMatrix ^ expected = gcnew ComplexMatrix(rownum, colnum);
			ComplexMatrix ^ actual = gcnew ComplexMatrix(rownum, colnum, 3);

			Assert::AreEqual(MatrixType::ComplexGeneralTridiagonal, actual->Type, "Test: Type");

			Random ^ randgen = gcnew Random();

			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					if (i == j || j - i == 1 || i - j == 1) {
						double real = randgen->NextDouble() * 10;
						double imag = randgen->NextDouble() * 10;

						expected[i, j] = Complex(real, imag);
						actual[i, j] = Complex(real, imag);
					} else {
						expected[i, j] = Complex(0.0, 0.0);
						actual[i, j] = Complex(0.0, 0.0);
					}
				}
			}

			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					Assert::AreEqual(expected[i, j].Real, actual[i, j].Real, 1e-10, "Test: Set/Get");	
					Assert::AreEqual(expected[i, j].Imag, actual[i, j].Imag, 1e-10, "Test: Set/Get");	
				}
			}
			
		};

		[Test]
		void TestSolve(){
			
			int rownum = 10;
			int colnum = 10;

			ComplexMatrix ^ trimat = gcnew ComplexMatrix(rownum, colnum, 3);
			Random ^ randgen = gcnew Random();
			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					if (i == j || j - i == 1 || i - j == 1) {
						double real = randgen->NextDouble() * 10;
						double imag = randgen->NextDouble() * 10;
						trimat[i, j] = Complex(real, imag);
					} else {
						trimat[i, j] = Complex(0.0, 0.0);
					}
				}
			}

			ComplexMatrix ^ expected = gcnew ComplexMatrix(rownum, 1);

			for (int i = 0; i < rownum; i++) {
				double real = randgen->NextDouble() * 10 - 5;
				double imag = randgen->NextDouble() * 10 - 5;
				expected[i, 0] = Complex(real, imag);	
			}

			ComplexMatrix ^ b = trimat * expected;

			//LUFactorization::Solve(trimat, b);
			LinearEquation::Solve(trimat, b);

			for (int i = 0; i < rownum; i++) {
				Assert::AreEqual(expected[i, 0].Real, b[i, 0].Real, 1e-10, "Test: Solve.");
				Assert::AreEqual(expected[i, 0].Imag, b[i, 0].Imag, 1e-10, "Test: Solve.");
			}
			

		};

		[Test]
		void TestFactorizedSolve() {
			
			int rownum = 10;
			int colnum = 10;

			ComplexMatrix ^ trimat = gcnew ComplexMatrix(rownum, colnum, 3);
			Random ^ randgen = gcnew Random();
			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					if (i == j || j - i == 1 || i - j == 1) {
						double real = randgen->NextDouble() * 10;
						double imag = randgen->NextDouble() * 10;
						trimat[i, j] = Complex(real, imag);
					} else {
						trimat[i, j] = Complex(0.0, 0.0);
					}
				}
			}

			ComplexMatrix ^ expected = gcnew ComplexMatrix(rownum, 1);

			for (int i = 0; i < rownum; i++) {
				double real = randgen->NextDouble() * 10;
				double imag = randgen->NextDouble() * 10;
				expected[i, 0] = Complex(real, imag);	
			}

			ComplexMatrix ^ b = trimat * expected;

			//LUFactorization::Factorize(trimat);
			LinearEquation::Factorize(trimat);

			//LUFactorization::Solve(trimat, b);
			LinearEquation::Solve(trimat, b);

			for (int i = 0; i < rownum; i++) {
				Assert::AreEqual(expected[i, 0].Real, b[i, 0].Real, 1e-10, "Test: Factorized Solve.");
				Assert::AreEqual(expected[i, 0].Imag, b[i, 0].Imag, 1e-10, "Test: Factorized Solve.");
			}


		};

		[Test]
		void TestRepeatedFactorizedSolve() {
			
			int rownum = 10;
			int colnum = 10;

			ComplexMatrix ^ trimat = gcnew ComplexMatrix(rownum, colnum, 3);
			Random ^ randgen = gcnew Random();
			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					if (i == j || j - i == 1 || i - j == 1) {
						double real = randgen->NextDouble() * 10;
						double imag = randgen->NextDouble() * 10;
						trimat[i, j] = Complex(real, imag);
					} else {
						trimat[i, j] = Complex(0.0, 0.0);
					}
				}
			}

			ComplexMatrix ^ expected = gcnew ComplexMatrix(rownum, 1);

			for (int i = 0; i < rownum; i++) {
				double real = randgen->NextDouble() * 10;
				double imag = randgen->NextDouble() * 10;
				expected[i, 0] = Complex(real, imag);	
			}

			ComplexMatrix ^ b = trimat * expected;

			ComplexMatrix ^ expected2 = gcnew ComplexMatrix(rownum, 1);

			for (int i = 0; i < rownum; i++) {
				double real = randgen->NextDouble() * 10;
				double imag = randgen->NextDouble() * 10;
				expected2[i, 0] = Complex(real, imag);	
			}

			ComplexMatrix ^ b2 = trimat * expected2;

			//LUFactorization::Factorize(trimat);
			LinearEquation::Factorize(trimat);

			//LUFactorization::Solve(trimat, b);
			LinearEquation::Solve(trimat, b);

			//LUFactorization::Solve(trimat, b2);
			LinearEquation::Solve(trimat, b2);

			for (int i = 0; i < rownum; i++) {
				Assert::AreEqual(expected[i, 0].Real, b[i, 0].Real, 1e-10, "Test: Factorized Solve.");
				Assert::AreEqual(expected[i, 0].Imag, b[i, 0].Imag, 1e-10, "Test: Factorized Solve.");
			}
			for (int i = 0; i < rownum; i++) {
				Assert::AreEqual(expected2[i, 0].Real, b2[i, 0].Real, 1e-10, "Test: Factorized Solve.");
				Assert::AreEqual(expected2[i, 0].Imag, b2[i, 0].Imag, 1e-10, "Test: Factorized Solve.");
			}


		};

		[Test]
		void TestRepeatedSolve() {
			
			int rownum = 10;
			int colnum = 10;

			ComplexMatrix ^ trimat = gcnew ComplexMatrix(rownum, colnum, 3);
			Random ^ randgen = gcnew Random();
			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					if (i == j || j - i == 1 || i - j == 1) {
						double real = randgen->NextDouble() * 10;
						double imag = randgen->NextDouble() * 10;
						trimat[i, j] = Complex(real, imag);
					} else {
						trimat[i, j] = Complex(0.0, 0.0);
					}
				}
			}

			ComplexMatrix ^ expected = gcnew ComplexMatrix(rownum, 1);

			for (int i = 0; i < rownum; i++) {
				double real = randgen->NextDouble() * 10;
				double imag = randgen->NextDouble() * 10;
				expected[i, 0] = Complex(real, imag);	
			}

			ComplexMatrix ^ b = trimat * expected;

			ComplexMatrix ^ expected2 = gcnew ComplexMatrix(rownum, 1);

			for (int i = 0; i < rownum; i++) {
				double real = randgen->NextDouble() * 10;
				double imag = randgen->NextDouble() * 10;
				expected2[i, 0] = Complex(real, imag);	
			}

			ComplexMatrix ^ b2 = trimat * expected2;

			//LUFactorization::Solve(trimat, b);
			LinearEquation::Solve(trimat, b);

			for (int i = 0; i < rownum; i++) {
				Assert::AreEqual(expected[i, 0].Real, b[i, 0].Real, 1e-10, "Test: Factorized Solve.");
				Assert::AreEqual(expected[i, 0].Imag, b[i, 0].Imag, 1e-10, "Test: Factorized Solve.");
			}
			//for (int i = 0; i < rownum; i++) {
			//	Assert::AreEqual(expected2[i, 0].Real, b2[i, 0].Real, 1e-10, "Test: Factorized Solve.");
			//	Assert::AreEqual(expected2[i, 0].Imag, b2[i, 0].Imag, 1e-10, "Test: Factorized Solve.");
			//}


		};

		[Test]
		void TestClone() {
			
			int rownum = 10;
			int colnum = 10;

			ComplexMatrix ^ trimat = gcnew ComplexMatrix(rownum, colnum, 3);
			ComplexMatrix ^ expected = gcnew ComplexMatrix(rownum, colnum);

			Random ^ randgen = gcnew Random();
			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					if (i == j || j - i == 1 || i - j == 1) {
						double real = randgen->NextDouble() * 10;
						double imag = randgen->NextDouble() * 10;

						trimat[i, j] = Complex(real, imag);
						expected[i, j] = Complex(real, imag);
					} else {
						trimat[i, j] = Complex(0.0, 0.0);
						expected[i, j] = Complex(0.0, 0.0);
					}
				}
			}

			ComplexMatrix ^ trimatclone = trimat->Clone();

			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					if (i == j || j - i == 1 || i - j == 1) {
						double real = randgen->NextDouble() * 10;
						double imag = randgen->NextDouble() * 10;

						trimat[i, j] = Complex(real, imag);
					} else {
						trimat[i, j] = Complex(0.0, 0.0);
					}
				}
			}

			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					Assert::AreEqual(expected[i, j].Real, trimatclone[i, j].Real, 1e-20, "Test: Clone");
					Assert::AreEqual(expected[i, j].Imag, trimatclone[i, j].Imag, 1e-20, "Test: Clone");
				}
			}

			Assert::AreEqual(expected->IsFactorized, trimatclone->IsFactorized, "Test: Clone");

			//LUFactorization::Factorize(trimat);
			LinearEquation::Factorize(trimat);

			trimatclone = trimat->Clone();

			Assert::AreEqual(trimat->IsFactorized, trimatclone->IsFactorized, "Test: Clone");



		};


	};
}
